#include "stm32f10x.h"                  // Device header
#include "Timer.h"
#include "OLED.h"
#include "led.h"
uint32_t second = 0;
uint32_t minute = 0;
uint32_t hour = 0;
/**
* @brief  进行内部时钟驱动的定时器初始化
	* @param  RCC_APB1Periph: 最常用的通用定时器（可能只有部分，如TIM2）是APB1总线的外设
  * @param  TIMx: 选择时基单元的时钟，如果与上面的参数相对应就是使用的内部时钟
	* @param  TIM_ClockDivision：滤波器采样频率分频值。设置得越高，频率越低，采样点数越多，滤波效果越好，但
						信号延迟也会更大。根据需要选择合适值，若输入的波形可以确保其比较稳定就可以随便配置。参见
						TIM_Clock_Division_CKD
	* @param  TIM_CounterMode：计数模式，分向上、向下和中央对齐三种。参见TIM_Counter_Mode
	* @param  TIM_Period：计时器时钟
	* @param  TIM_Prescaler：预分频器时钟
	
	以上两个参数共同决定定时频率，计算公式为定时频率72MHz/(TIM_Period+1)/(TIM_Prescaler+1)
	
	* @param  TIM_RepetitionCounter：配置重复计数器。这个是只有高级计数器才有的，用不到的话直接输入一个0就行
	* @param  TIM_IT：选择中断。（取值情况参照TIM_ITConfig，可选择更新中断）
	* @param  NVIC_PriorityGroup：分组模式（参照NVIC_PriorityGroupConfig，其实对于一个单片机而言，中断的分组方式应当是唯
	一的，要确保所有可能填写分组方式的该参数都是同一个选项，或者干脆不要把这个函数放进任何模块内，只在主函数里执行一次即可）
	* @param  NVIC_IRQChannel：中断通道（参考IRQn_Type，搜索时将范围扩大到整个工程，搜出来的文件是对于所有
	stm32型号的，认准第243行的F10X_MD里的选项,要与具体选用哪个计时器相对应，比如选计时器TIM2这里就填TIM2_IRQn）
	* @param  NVIC_IRQChannelPreemptionPriority、IRQChannelSubPriority：分别为抢占优先级和相应优先级，参考NVIC_Priority_Table
  * @retval None
	
	另外，根据stm32f10x_tim.c第266行的说明，在函数TIM_TimeBaseInit即将执行完时，会立刻生成一个更新事件，用于重新装载预分频器与重复计数器的值，比如说
	如果从零开始计数，上电后会马上计一个1，根本观察不到0（但我好像没有真的观察到这个现象欸，不知道为什么）
  */

void Internal_Timer_Init(uint32_t RCC_APB1Periph, TIM_TypeDef* TIMx, uint16_t TIM_ClockDivision, uint16_t 
	TIM_CounterMode, uint16_t TIM_Period, uint16_t TIM_Prescaler, uint8_t TIM_RepetitionCounter, uint16_t 
TIM_IT, uint32_t NVIC_PriorityGroup, IRQn_Type NVIC_IRQChannel, uint8_t IRQChannelPreemptionPriority, 
uint8_t IRQChannelSubPriority)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph, ENABLE);//开启时钟
	TIM_InternalClockConfig(TIMx);//配置内部时钟
	TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure;
	TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_ClockDivision;
	TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode;
	TIM_TimerBaseInitStructure.TIM_Period = TIM_Period;
	TIM_TimerBaseInitStructure.TIM_Prescaler = TIM_Prescaler;
	TIM_TimerBaseInitStructure.TIM_RepetitionCounter = TIM_RepetitionCounter;
	TIM_TimeBaseInit(TIMx, &TIM_TimerBaseInitStructure);//配置计时器时基
	TIM_ClearFlag(TIMx, TIM_CounterMode);//清除更新事件置的中断标志位，让计数可以正常地从0开始.无论如何还是加这一句吧
	TIM_ITConfig(TIMx, TIM_IT, ENABLE);//配置使能中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = NVIC_IRQChannel;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = IRQChannelPreemptionPriority;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = IRQChannelSubPriority;
  NVIC_Init(&NVIC_InitStruct);//配置NVIC
	TIM_Cmd(TIMx, ENABLE);//启动定时器
}

/**
* @brief  进行外部时钟驱动的定时器初始化
	* @param  RCC_APB2Periph_GPIO：将会用于传入外部触发信号的GPIO引脚的时钟
  * @param  GPIOx: 哪一片GPIO（参照GPIO_Init）
  * @param  pin：一片GPIO中具体哪个引脚（参照结构体GPIO_InitTypeDef中的定义）
	* @param  mode：输入模式。官方推荐是选用浮空输入模式，但这可能会使得输出乱跳。建议选用上拉输入，只要输入信号不是太小，不足以使得上拉电阻对其产生
						影响即可
	* @param  RCC_APB1Periph: 最常用的通用定时器（可能只有部分，如TIM2）是APB1总线的外设
  * @param  TIMx: 选择时基单元的时钟，如果与上面的参数相对应就是使用的内部时钟
  * @param  TIM_ExtTRGPrescaler：外部触发预分频器（参见TIM_ETRClockMode2Config）
	* @param  TIM_ExtTRGPolarity:外部触发的极性
	* @param  ExtTRGFilter：外部触发滤波器，值必须在0x00到0x0F之间，可以选择0x0f以达到良好的滤波效果，详见芯片手册14.4.2：ETF[3:0]
	* @param  TIM_ClockDivision：滤波器采样频率分频值。设置得越高，频率越低，采样点数越多，滤波效果越好，但
						信号延迟也会更大。根据需要选择合适值，若输入的波形可以确保其比较稳定就可以随便配置。参见
						TIM_Clock_Division_CKD
	* @param  TIM_CounterMode：计数模式，分向上、向下和中央对齐三种。参见TIM_Counter_Mode
	* @param  TIM_Period：计时器时钟
	* @param  TIM_Prescaler：预分频器时钟
	
	以上两个参数共同决定定时频率，计算公式为定时频率72MHz/(TIM_Period+1)/(TIM_Prescaler+1)
	
	* @param  TIM_RepetitionCounter：配置重复计数器。这个是只有高级计数器才有的，用不到的话直接输入一个0就行
	* @param  TIM_IT：选择中断。（取值情况参照TIM_ITConfig，可选择更新中断）
	* @param  NVIC_PriorityGroup：分组模式（参照NVIC_PriorityGroupConfig，其实对于一个单片机而言，中断的分组方式应当是唯
	一的，要确保所有可能填写分组方式的该参数都是同一个选项，或者干脆不要把这个函数放进任何模块内，只在主函数里执行一次即可）
	* @param  NVIC_IRQChannel：中断通道（参考IRQn_Type，搜索时将范围扩大到整个工程，搜出来的文件是对于所有
	stm32型号的，认准第243行的F10X_MD里的选项,要与具体选用哪个计时器相对应，比如选计时器TIM2这里就填TIM2_IRQn）
	* @param  NVIC_IRQChannelPreemptionPriority、IRQChannelSubPriority：分别为抢占优先级和相应优先级，参考NVIC_Priority_Table
  * @retval None
	
	另外，根据stm32f10x_tim.c第266行的说明，在函数TIM_TimeBaseInit即将执行完时，会立刻生成一个更新事件，用于重新装载预分频器与重复计数器的值，比如说
	如果从零开始计数，上电后会马上计一个1，根本观察不到0（但我好像没有真的观察到这个现象欸，不知道为什么）
  */

void External_Timer_Init(uint32_t RCC_APB2Periph_GPIO, GPIO_TypeDef* GPIOx, uint16_t pin, GPIOMode_TypeDef mode, uint32_t RCC_APB1Periph, 
	TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter, uint16_t TIM_ClockDivision, uint16_t 
	TIM_CounterMode, uint16_t TIM_Period, uint16_t TIM_Prescaler, uint8_t TIM_RepetitionCounter, uint16_t TIM_IT, uint32_t NVIC_PriorityGroup, 
	IRQn_Type NVIC_IRQChannel, uint8_t IRQChannelPreemptionPriority, uint8_t IRQChannelSubPriority)
{
	gpio_Init(RCC_APB2Periph_GPIO, GPIOx, pin, mode);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph, ENABLE);//开启时钟
	TIM_ETRClockMode2Config(TIMx, TIM_ExtTRGPrescaler, TIM_ExtTRGPolarity, ExtTRGFilter);;//配置外部时钟模式2
	TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure;
	TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_ClockDivision;
	TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode;
	TIM_TimerBaseInitStructure.TIM_Period = TIM_Period;
	TIM_TimerBaseInitStructure.TIM_Prescaler = TIM_Prescaler;
	TIM_TimerBaseInitStructure.TIM_RepetitionCounter = TIM_RepetitionCounter;
	TIM_TimeBaseInit(TIMx, &TIM_TimerBaseInitStructure);//配置计时器时基
	TIM_ClearFlag(TIMx, TIM_CounterMode);//清除更新事件置的中断标志位，让计数可以正常地从0开始。无论如何还是加这一句吧
	TIM_ITConfig(TIMx, TIM_IT, ENABLE);//配置使能中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup);
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = NVIC_IRQChannel;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = IRQChannelPreemptionPriority;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = IRQChannelSubPriority;
  NVIC_Init(&NVIC_InitStruct);//配置NVIC
	TIM_Cmd(TIMx, ENABLE);//启动定时器
}

void TIM2_IRQHandler()//start->startup_stm32f10x_md.s文件内第61行起为中断向量表，第78行起以IRQHandler结尾
	//的为外部中断的向量表，按照中断路径选取对应的函数。此处以TIM2为例，选择更新中断模式。
{
	//在执行中断之前需要先用标志位判断是否是我们选择的更新中断模式下的TIM2引发了中断
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		OLED_ShowNum(1, 1, hour, 2);
		OLED_ShowNum(1, 4, minute, 2);
		OLED_ShowNum(1, 7, second, 2);
		second++;
		if(second == 60)
		{
			second = 0;
			minute++;
		}
		if(minute == 60)
		{
			minute = 0;
			hour++;
		}
		if(hour == 24)
		{
			hour = 0;
		}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//中断函数执行完后需要手动受释放标志位，否则单片机会不断相应中断，最终卡死
	}
}

